/*
 * The MIT License (MIT)
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 */

use napi::{bindgen_prelude::*, JsArrayBuffer, JsBuffer, JsTypedArray};
use napi_derive::napi;

#[napi]
fn get_buffer() -> Buffer {
  String::from("Hello world").as_bytes().into()
}

#[napi]
fn append_buffer(buf: Buffer) -> Buffer {
  let mut buf = Vec::<u8>::from(buf);
  buf.push(b'!');
  buf.into()
}

#[napi]
fn get_empty_buffer() -> Buffer {
  vec![].into()
}

#[napi]
fn convert_u32_array(input: Uint32Array) -> Vec<u32> {
  input.to_vec()
}

#[napi]
fn create_external_typed_array() -> Uint32Array {
  Uint32Array::new(vec![1, 2, 3, 4, 5])
}

#[napi]
fn mutate_typed_array(mut input: Float32Array) {
  for item in input.as_mut() {
    *item *= 2.0;
  }
}

#[napi]
fn deref_uint8_array(a: Uint8Array, b: Uint8ClampedArray) -> u32 {
  (a.len() + b.len()) as u32
}

#[napi]
async fn buffer_pass_through(buf: Buffer) -> Result<Buffer> {
  Ok(buf)
}

#[napi]
async fn array_buffer_pass_through(buf: Uint8Array) -> Result<Uint8Array> {
  Ok(buf)
}

#[napi]
fn accept_slice(fixture: &[u8]) -> usize {
  fixture.len()
}

#[napi]
fn accept_arraybuffer(fixture: JsArrayBuffer) -> Result<usize> {
  Ok(fixture.into_value()?.as_ref().len())
}

#[napi]
fn create_arraybuffer(env: Env) -> Result<JsArrayBuffer> {
  let buf = env.create_arraybuffer_with_data(vec![1, 2, 3, 4])?;
  Ok(buf.value)
}

#[napi]
fn create_empty_arraybuffer_with_data(env: Env) -> Result<JsArrayBuffer> {
  let buf = env.create_arraybuffer_with_data(vec![])?;
  Ok(buf.value)
}

#[napi]
fn create_empty_arraybuffer(env: Env) -> Result<JsArrayBuffer> {
  let buf = env.create_arraybuffer(0)?;
  Ok(buf.value)
}

#[napi]
fn u8_array_to_array(input: &[u8]) -> Vec<u8> {
  input.to_vec()
}

#[napi]
fn i8_array_to_array(input: &[i8]) -> Vec<i8> {
  input.to_vec()
}

#[napi]
fn u16_array_to_array(input: &[u16]) -> Vec<u16> {
  input.to_vec()
}

#[napi]
fn i16_array_to_array(input: &[i16]) -> Vec<i16> {
  input.to_vec()
}

#[napi]
fn u32_array_to_array(input: &[u32]) -> Vec<u32> {
  input.to_vec()
}

#[napi]
fn i32_array_to_array(input: &[i32]) -> Vec<i32> {
  input.to_vec()
}

#[napi]
fn f32_array_to_array(input: &[f32]) -> Vec<f32> {
  input.to_vec()
}

#[napi]
fn f64_array_to_array(input: &[f64]) -> Vec<f64> {
  input.to_vec()
}

#[napi]
fn u64_array_to_array(input: &[u64]) -> Vec<u64> {
  input.to_vec()
}

#[napi]
fn i64_array_to_array(input: &[i64]) -> Vec<i64> {
  input.to_vec()
}

#[napi]
fn accept_uint8_clamped_slice(input: Uint8ClampedSlice) -> usize {
  input.len()
}

#[napi]
fn accept_uint8_clamped_slice_and_buffer_slice(a: BufferSlice, b: Uint8ClampedSlice) -> usize {
  a.len() + b.len()
}

struct AsyncBuffer {
  buf: Buffer,
}

#[napi]
impl Task for AsyncBuffer {
  type Output = u32;
  type JsValue = u32;

  fn compute(&mut self) -> Result<Self::Output> {
    Ok(self.buf.iter().fold(0u32, |a, b| a + *b as u32))
  }

  fn resolve(&mut self, _env: Env, output: Self::Output) -> Result<Self::JsValue> {
    Ok(output)
  }
}

#[napi]
fn async_reduce_buffer(buf: Buffer) -> Result<AsyncTask<AsyncBuffer>> {
  Ok(AsyncTask::new(AsyncBuffer { buf }))
}

#[napi]
fn async_buffer_to_array(buf: JsArrayBuffer) -> Result<Vec<u8>> {
  Ok(buf.into_value()?.as_ref().to_vec())
}

#[napi]
fn js_array_length(input: JsTypedArray) -> Result<usize> {
  let res = input.into_value()?;
  Ok(res.length)
}

#[napi]
fn jsbuffer_sum(buf: JsBuffer) -> Result<u8> {
  let value = buf.into_value()?;
  let sum = value.iter().fold(0, |acc, x| acc + x);

  Ok(sum)
}

#[napi]
fn mutate_i64_typed_array(mut buf: BigInt64Array) {
  for dat in buf.as_mut() {
    *dat += 1;
  }
}

#[napi]
fn mutate_u64_typed_array(mut buf: BigUint64Array) {
  for dat in buf.as_mut() {
    *dat += 1;
  }
}

#[napi]
fn create_empty_typed_array() -> Uint32Array {
  Uint32Array::new(vec![])
}
